#include <iostream>
#include <functional>
#include <memory>
#include <string>
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Calculate.hpp"

using namespace std;

void Usage(const string &proc)
{
    cout << "Usage:\n\t" << proc << " serverport" << endl;
}

using callback_t=function<Response(const Request& req)>;

class CalService
{
public:
    CalService(callback_t cb)
    :_cb(cb)
    {

    }

    void Service(socket_sptr sockptr, InetAddr client)
    {
        int sockfd = sockptr->SockFd();
        LOG(DEBUG, "get a new link, info %s:%d, sockfd : %d", client.IP().c_str(), client.Port(), sockfd);

        string clientaddr = "[" + client.IP() + ":" + to_string(client.Port()) + "]# ";
        string message;
        while (true)
        {
            Request req;
            //1.读取数据
            int n = sockptr->Recv(&message); // 如何保证Recv()获取到的字符串是一个完整的request?
            if (n < 0)
            {
                LOG(DEBUG, "client %s quit", clientaddr.c_str());
                break;
            }
            //2.保证字符串是一个完整的请求(发送数据时添加协议报头,接收数据时对收到的报文进行判定)
            //"len"\r\n"{json_str}"\r\n
            // string Decode(string& message)
            string package = Decode(message);
            if (package.empty())
            {
                // 没有一个完整的请求,继续从接收缓冲区中读取
                continue;
            }
            //3.有效载荷必定完整&&对字符串反序列化
            std::cout<<"服务端接收到客户端的请求:\n"<<package<<std::endl;
            req.Deserialize(package);
            //4.业务处理
            Response resp=_cb(req);
            //5. 将Response转换为字符串,序列化
            string send_str;
            resp.Serialize(&send_str);
            std::cout<<"响应序列化:\n"<<send_str<<std::endl;
            //6. 为保证发送的报文是一条完整的报文,添加协议报头 "len"\r\n"{json_str}"\r\n
            send_str=Encode(send_str);

            //7.发送数据
            sockptr->Send(send_str);//发送数据暂时不做处理
        }
    }
private:
    callback_t _cb;
};

//./cal_server 8888
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
    }
    uint16_t port = stoi(argv[1]);

    //计算模块
    Calculate cal;
    CalService calservice(std::bind(&Calculate::Excute,&cal,std::placeholders::_1));
    io_service_t Service=std::bind(&CalService::Service,&calservice,std::placeholders::_1,std::placeholders::_2);
    unique_ptr<TcpServer> tsvr(new TcpServer(port, Service));
    tsvr->Start();

    return 0;
}